/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package commands;

import cmdexceptions.OptionArgumentRequiredException;
import cmdexceptions.OptionException;
import cmdexceptions.RequiredOptionException;
import cmdexceptions.UnknownOptionException;
import general.IOption;
import general.OptionBase;

import java.io.PrintStream;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 *
 * eCOM - Adopte un Presta - Mars-Avril 2013
 * @author Kheira, Jef, William, Tuan-Long
 */
public abstract class CommandAbstract implements ICommand {
    
	protected Map<String, IOption> options = new HashMap<String, IOption>();
	protected Map<String, IOption> optionsByName = new HashMap<String, IOption>();
	protected Map<IOption, String> values;
	protected Deque<String> dataArguments;


	@Override
	public final void execute(List<String> args, 
                                    PrintStream out, 
                                    PrintStream err) 
                throws Exception 
        {
		dataArguments = new LinkedList<String>();
		values = new LinkedHashMap<IOption, String>();

		try 
                {
			this.parse(args.toArray(new String[args.size()]));	
			this.execute(out, err);

		} 
                catch (OptionException ex) 
                {
                    err.println(ex.getMessage());
		}

	}


	protected abstract void execute(PrintStream out, PrintStream err) throws Exception;


	protected void addOption(IOption option) {
		options.put("-" + option.getShortName(), option);
		options.put("--" + option.getName(), option);
		optionsByName.put(option.getName(), option);
	}


    // Méthode pour parser les options
    protected final void parse(String[] argv) throws OptionException 
    {
        int position = 0;
        
        while (position < argv.length) 
        {
            String curArg = argv[position];
            if (curArg.startsWith("-")) 
            {			
                if (curArg.equals("--")) 
                {
                    position += 1;
                    break;
                }		
                String valueArg = null;
                if (curArg.startsWith("--")) 
                {			
                    int equalsPos = curArg.indexOf("=");
                    if (equalsPos != -1) 
                    {
                        valueArg = curArg.substring(equalsPos+1);
                        curArg = curArg.substring(0,equalsPos);
                    }			
                } 
                else if (curArg.length() > 2) 
                {			
                    for(int i=1; i < curArg.length(); i++) 
                    {
                        IOption opt = this.options.get("-" + curArg.charAt(i));			
                        if(opt == null) 
                        {
                            IOption unknowOption = new OptionBase(curArg, false);
                            throw new UnknownOptionException(unknowOption);
                        }
                        if(!opt.isSwitch()) 
                        {
                            throw new OptionArgumentRequiredException(opt);
			}
                        values.put(opt, null);
                    }					
                    position++;		
                    continue;
                }

                IOption opt = this.options.get(curArg);
                if (opt == null) 
                {
                    IOption unknowOption = new OptionBase(curArg, false);
                    throw new UnknownOptionException(unknowOption);
                }
				
                String value = null;
                if (!opt.isSwitch()) 
                {
                    if (valueArg == null) 
                    {
                        position += 1;
                        if (position < argv.length) 
                        {
                            valueArg = argv[position];
                        }
                    }
                    value = valueArg;
		
                    if (value == null) 
                    {
                        throw new OptionArgumentRequiredException(opt);
                    }
                }
                values.put(opt, value);

                position += 1;
            }
            else 
            {
                dataArguments.add(curArg);
                position += 1;
            }
        }
		
        for ( ; position < argv.length; ++position ) 
        {
            dataArguments.add(argv[position]);
        }
        
	for(IOption option : optionsByName.values()) 
        {
            if (option.isRequired() && !values.containsKey(option)) 
            {
                throw new RequiredOptionException(option);
            }
        }
    }

	
	protected boolean hasOption(String optionName) 
        {
            return this.optionsByName.containsKey(optionName) 
                        && values.containsKey(this.optionsByName.get(optionName));
	}

	protected boolean hasOption(IOption option) 
        {
            return this.optionsByName.containsValue(option) 
                        && values.containsKey(option);
	}

	protected boolean getBooleanOption(String optionName) 
        {
            return hasOption(optionName)
                        && this.optionsByName.get(optionName).isSwitch();
	}

	
	protected int getIntegerOption(String optionName, int defaultValue) 
        {
		int returnValue = defaultValue;

		if (hasOption(optionName)) 
                {
                    try 
                    {
                        returnValue = Integer.parseInt(values.get(optionsByName.get(optionName)));
                    } 
                    catch(NumberFormatException e) {}
		}

		return returnValue;
	}

	protected String getStringOption(String optionName, String defaultValue) 
        {
		String res = defaultValue;

		if (hasOption(optionName)) 
                {
                    res = values.get(optionsByName.get(optionName));
		}
		return res;
	}
}
